﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.IO;
using System.Xml;

using Components.Base.ComponentInterface;

//includes for seans graphics compoents
using SunXNA.RainbowXNA;
using SunXNA;
using SunXNA.CameraManager;
using NeedsMoreNinja.EventManager;
using Cameras;

//
using Sound;
using Input;

// include for entities
using Darwin.Source.Components.World.Entities;
using Darwin.Source.Components.World.Entities.BuildingTypes;

namespace Darwin.Source.Components.World
{
    class World : ComponentInterface
    {
        static float constructionRangeLimiter = 5.0f;

        private int m_WorldSize;
        private int m_WorldLowest;
        private int m_WorldHeighest;
        private SFillWorld fillWorldEvent;

        public static uint m_UniversalID = 0;

        // Dictionaries
        public Dictionary<uint, CBuilding> m_buildingDictionary;
        public Dictionary<uint, CEntity> m_entityDictionary;
        public Dictionary<uint, CBaseResource> m_resourceDictionary;

        List<Darwin.Source.Components.World.Team.CTeam> m_teams;
        
        private CRainbowXNA.ViewportObjectHandleType m_viewportHandle;
        private CCameraManager.CameraHandle[] m_cameras;
        private int m_currentCamera;

        public List<CRainbowXNA.RenderObjectHandleType> m_LoadedModels;

        public CRainbowXNA.TextureObjectHandleType m_potentialFeild;

        public bool m_GameOver;
        public bool m_WinRar;

        public World()
        {
            //these are hard coded...which is a bitch...but nevermind
            m_WorldSize = 200;
            m_WorldLowest = 30;
            m_WorldHeighest = 200;

            fillWorldEvent = new SFillWorld();

            m_buildingDictionary = new Dictionary<uint, CBuilding>();
            m_entityDictionary = new Dictionary<uint, CEntity>(); ;
            m_resourceDictionary = new Dictionary<uint, CBaseResource>();

            m_cameras = new CCameraManager.CameraHandle[2];
            m_currentCamera = 0;

            m_teams = new List<Darwin.Source.Components.World.Team.CTeam>();

            m_LoadedModels = new List<CRainbowXNA.RenderObjectHandleType>();

            PotentialFeild.CPotentialFeildVector.InitColours();

            m_GameOver = false;
            m_WinRar = false;
        }

        public void Initialise()
        {
            Darwin.Source.Components.World.CEntityControllerPlayer playerController = new Darwin.Source.Components.World.CEntityControllerPlayer();
            playerController.Cursor = new CCursor();
            playerController.SetWorldRef(this);

            Darwin.Source.Components.World.CEntityControllerAI aiController = new Darwin.Source.Components.World.CEntityControllerAI();

            m_teams.Add(new Darwin.Source.Components.World.Team.CTeam(playerController, this, Team.Side.Darwin));

            m_teams.Add(new Darwin.Source.Components.World.Team.CTeam(aiController, this, Team.Side.Creationists));

            m_cameras[0] = Singleton<CCameraManager>.Instance.NewCamera<CGameCamera>();
            ((CGameCamera)m_cameras[0].Camera).Cursor = playerController.Cursor;

            m_cameras[1] = Singleton<CCameraManager>.Instance.NewCamera<CFreeCam>();

            Rectangle areaA;
            areaA.X = 0;
            areaA.Y = 0;
            areaA.Width = 1280;
            areaA.Height = 720;

            //at the very end of load world we create the viewport!!!
            m_viewportHandle = Singleton<CRainbowXNA>.Instance.CreateViewport(areaA, m_cameras[m_currentCamera], Color.Black);
        }

        #region Content loader, its big and ugly
        public override void loadContent(ContentManager content, String xmlFile)
        {
            StreamReader reader = new StreamReader(xmlFile);
            XmlDocument machineState = new XmlDocument();
            machineState.LoadXml(reader.ReadToEnd());
            reader.Close();

            //now start reading from the xml
            XmlNode root = machineState.SelectSingleNode("World");

            //get all the children from the node!!
            XmlNodeList everything = root.ChildNodes;

            //takes in the terrain heightmap from a file.
            Singleton<SunXNA.Terrain.CTerrain>.Create();
            Texture2D heightMap = content.Load<Texture2D>(everything[0].ChildNodes[0].InnerText.Trim());//height map
            float cellWidth = (float)Convert.ToDouble(everything[0].ChildNodes[1].InnerText.Trim());
            float cellDepth = (float)Convert.ToDouble(everything[0].ChildNodes[2].InnerText.Trim());
            float maxLandHeight = (float)Convert.ToDouble(everything[0].ChildNodes[3].InnerText.Trim());
            float minLandHeight = (float)Convert.ToDouble(everything[0].ChildNodes[4].InnerText.Trim());
            float waterDepth = (float)Convert.ToDouble(everything[0].ChildNodes[5].InnerText.Trim());
            //now the xna has been loaded we can create the terrain!!
            Singleton<SunXNA.Terrain.CTerrain>.Instance.Initialise(heightMap, minLandHeight, maxLandHeight, waterDepth, cellWidth, cellDepth, everything[0].ChildNodes[0].InnerText.Trim(), "GameData/Images/Water");

            //loads int he model for the skybox and loads it into rainbox Xna#
            Singleton<CRainbowXNA>.Instance.SetSkybox(everything[1].FirstChild.InnerText.Trim());
            
            //we now load the model list so that we have models to use for the world stuff
            //we keep a quick list of the render handles 
            
            foreach(XmlNode node in everything[2])
            {
                //each model in world is added to the handle vector, each model will know where its model is loaded
                m_LoadedModels.Add(Singleton<CRainbowXNA>.Instance.LoadStaticMesh(node.InnerText.Trim()));
            }
            
            //now we load the world map, which includes buildings and resources.
            Texture2D worldMap = content.Load<Texture2D>(everything[3].FirstChild.InnerText.Trim());//height map
            //we now suse this to create trees

            Color[] data = new Color[worldMap.Width * worldMap.Height];
            worldMap.GetData(data);

            uint index = 0;
            for (int x = 0; x < worldMap.Width; ++x)
            {
                for(int y = 0; y < worldMap.Height; ++y)
                {
                    if (data[x + y * worldMap.Width].G == 255)
                    {
                        Vector3 v3Pos = new Vector3(-m_WorldSize + x, 6, -m_WorldSize + y);
                        Vector3 v3Normal;
                        v3Pos.Y = Singleton<SunXNA.Terrain.CTerrain>.Instance.GetHeightAtXZ(v3Pos, out v3Normal) + 6;

                        CBaseResource tree = new CBaseResource(m_LoadedModels[1], eResourceType._Wood, v3Pos);
                        m_resourceDictionary.Add(++index, tree);

                        m_teams[0].AddPotentialFeild(Darwin.Source.Components.World.PotentialFeild.FeildType.Resources,
                                                     v3Pos,
                                                     -1.0f,
                                                     10000.0f);
                    }

                    if (data[x + y * worldMap.Width].R == 255)
                    {
                        Vector3 v3Pos = new Vector3(-m_WorldSize + x, 6, -m_WorldSize + y);
                        Vector3 v3Normal;
                        v3Pos.Y = Singleton<SunXNA.Terrain.CTerrain>.Instance.GetHeightAtXZ(v3Pos, out v3Normal) + 3;

                        m_teams[0].AddPotentialFeild(Darwin.Source.Components.World.PotentialFeild.FeildType.Home,
                                                     v3Pos,
                                                     -1.0f,
                                                     10000.0f);

                        Castle castle = new Castle();
                        castle.Initialise(m_LoadedModels[4]);
                        castle.Position = v3Pos;

                        m_buildingDictionary.Add(++index, castle);
                    }
                }
            }


            //at very end post the load event so rainbow knows whats going on
            SLoadContent loadEvent = new SLoadContent();
            CEventManager<SLoadContent>.PostEvent(loadEvent);
            //takes in a skybox filename and loads it into world.

            //CreatePotentialFeild();
        }

        private void CreatePotentialFeild()
        {
            CHeightMap heightMap = Singleton<SunXNA.Terrain.CTerrain>.Instance.HeightMap;

            m_potentialFeild = Singleton<CRainbowXNA>.Instance.CreateBlankTexture(heightMap.Width, heightMap.Depth);
            Texture2D image = m_potentialFeild.Texture;
            Vector4[] colours = new Vector4[heightMap.Width * heightMap.Depth];
            image.GetData<Vector4>(colours);

            const float minHeight = 0.11f;
            const float maxHeight = 0.4f;

            for (int x = 0; x < heightMap.Width; ++x)
            {
                for (int y = 0; y < heightMap.Depth; ++y)
                {
                    float heightPercentage = heightMap.GetHeightPercentage(x, y);

                    heightPercentage = (heightPercentage - minHeight) / (maxHeight - minHeight);

                    if (heightPercentage > 0.5f)
                    {
                        heightPercentage = 1.0f - heightPercentage;
                    }

                    colours[x + y * heightMap.Width].X = heightPercentage;
                    colours[x + y * heightMap.Width].Y = 0.0f;
                    colours[x + y * heightMap.Width].Z = 0.0f;
                    colours[x + y * heightMap.Width].W = 1.0f;
                }
            }

            image.SetData<Vector4>(colours);
        }

        #endregion

        public override void draw2D()
        {
            foreach (Team.CTeam team in m_teams)
            {
                team.Draw2D();
            }

            //Singleton<CRainbowXNA>.Instance.RenderTexture(m_potentialFeild);
        }

        public override void draw3D()
        {
            CEventManager<SFillWorld>.PostEvent(fillWorldEvent);

            foreach (KeyValuePair<uint,CBaseResource> pair in m_resourceDictionary)
            {
                pair.Value.DoRender();
            }

            foreach (KeyValuePair<uint, CBuilding> pair in m_buildingDictionary)
            {
                pair.Value.DoRender();
            }

            
        }

        public override void update(GameTime time)
        {
            SStep e;
            e.gameTime = time;
            CEventManager<SStep>.PostEvent(e);

            if (m_teams[0].CurrentResources < 20 && m_teams[0].Minions.Count == 0)
            {
                m_GameOver = true;
            }
            else
            {
                CEntityControllerAI AI = (CEntityControllerAI)m_teams[1].Controller;
                if (AI.spawned >= CEntityControllerAI.MaxSpawnLimit && m_teams[1].Minions.Count == 0)
                {
                    m_WinRar = true;
                }
            }
        }

        public override void unloadContent(ContentManager content)
        {

        }

        public int Size
        {
            get { return m_WorldSize; }
            set { m_WorldSize = value; }
        }

        public int LowestPoint
        {
            get { return m_WorldLowest; }
            set { m_WorldLowest = value; }
        }

        public int HighestPoint
        {
            get { return m_WorldHeighest; }
            set { m_WorldHeighest = value; }
        }

        public bool canCreateHere(Vector3 position)
        {
            foreach ( CEntity it_entities in m_entityDictionary.Values )
            {
                if (Vector3.Distance(it_entities.Position, position) < constructionRangeLimiter)
                    return false;
            }

            foreach ( CBuilding it_buildings in m_buildingDictionary.Values )
            {
                if (Vector3.Distance(it_buildings.Position, position) < constructionRangeLimiter)
                    return false;
            }
            return true;

        }
    }
}
